/*******************************************************************************
 * Copyright (c) 2004, 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal.dnd;

//import java.util.ArrayList;
//import java.util.Iterator;
//import java.util.List;

import org.eclipse.jface.util.Geometry;
//import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
//import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
//import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
//import org.eclipse.swt.widgets.Tracker;
//import org.eclipse.ui.PlatformUI;
//import org.eclipse.ui.internal.DragCursors;

/**
 * Provides the methods for attaching drag-and-drop listeners to SWT controls. 
 */
public class DragUtil {
// RAP [rh] DnD not implemented
//    private static final String DROP_TARGET_ID = "org.eclipse.ui.internal.dnd.dropTarget"; //$NON-NLS-1$

// RAP [rh] DnD not implemented
//    /**
//     * The location where all drags will end. If this is non-null, then
//     * all user input is ignored in drag/drop. If null, we use user input
//     * to determine where objects should be dropped.
//     */
//    private static TestDropLocation forcedDropTarget = null;
   
// RAP [rh] DnD not implemented    
//    /**
//     * List of IDragOverListener
//     */
//    private static List defaultTargets = new ArrayList();

// RAP [rh] DnD not implemented    
//    /**
//     * Sets the drop target for the given control. It is possible to add one or more 
//     * targets for a "null" control. This becomes a default target that is used if no
//     * other targets are found (for example, when dragging objects off the application
//     * window). 
//     * 
//     * @param control the control that should be treated as a drag target, or null
//     * to indicate the default target
//     * @param target the drag target to handle the given control
//     */
//    public static void addDragTarget(Control control, IDragOverListener target) {
//        if (control == null) {
//            defaultTargets.add(target);
//        } else {
//            List targetList = getTargetList(control);
//
//            if (targetList == null) {
//                targetList = new ArrayList(1);
//            }
//            targetList.add(target);
//            control.setData(DROP_TARGET_ID, targetList);
//        }
//    }

// RAP [rh] DnD not implemented    
//    /**
//     * Return the list of 'IDragOverListener' elements associated with
//     * the given control. If there's a 'global' listener then always
//     * return it.
//     * 
//     * @param control
//     * @return
//     */
//    private static List getTargetList(Control control) {
//        List result = (List) control.getData(DROP_TARGET_ID);
//        return result;
//    }

// RAP [rh] DnD not implemented    
//    /**
//     * Removes a drop target from the given control.
//     * 
//     * @param control
//     * @param target
//     */
//    public static void removeDragTarget(Control control,
//            IDragOverListener target) {
//        if (control == null) {
//            defaultTargets.remove(target);
//        } else {
//            List targetList = getTargetList(control);
//            if (targetList != null) {
//                targetList.remove(target);
//                if (targetList.isEmpty()) {
//                    control.setData(DROP_TARGET_ID, null);
//                }
//            }
//        }
//    }

    /**
     * Shorthand method. Returns the bounding rectangle for the given control, in
     * display coordinates. Note that all 'Shell' controls are expected to be 'top level'
     * so DO NOT do the origin offset for them.
     * 
     * @param draggedItem
     * @param boundsControl
     * @return
     */
    public static Rectangle getDisplayBounds(Control boundsControl) {
        Control parent = boundsControl.getParent();
        if (parent == null || boundsControl instanceof Shell) {
            return boundsControl.getBounds();
        }

        return Geometry.toDisplay(parent, boundsControl.getBounds());
    }

// RAP [rh] DnD not implemented    
//    public static boolean performDrag(final Object draggedItem,
//            Rectangle sourceBounds, Point initialLocation, boolean allowSnapping) {
//
//        IDropTarget target = dragToTarget(draggedItem, sourceBounds,
//                initialLocation, allowSnapping);
//
//        if (target == null) {
//            return false;
//        }
//
//        target.drop();
//
//        // If the target can handle a 'finished' notification then send one
//        if (target!= null && target instanceof IDropTarget2) {
//        	((IDropTarget2)target).dragFinished(true);
//        }
//
//        return true;
//    }

// RAP [rh] DnD not implemented    
//    /**
//     * Drags the given item to the given location (in display coordinates). This
//     * method is intended for use by test suites.
//     * 
//     * @param draggedItem object being dragged
//     * @param finalLocation location being dragged to
//     * @return true iff the drop was accepted
//     */
//    public static boolean dragTo(Display display, Object draggedItem,
//            Point finalLocation, Rectangle dragRectangle) {
//        Control currentControl = SwtUtil.findControl(display, finalLocation);
//
//        IDropTarget target = getDropTarget(currentControl, draggedItem,
//                finalLocation, dragRectangle);
//
//        if (target == null) {
//            return false;
//        }
//
//        target.drop();
//
//        return true;
//    }

// RAP [rh] DnD not implemented
//    /**
//     * Forces all drags to end at the given position (display coordinates). Intended
//     * for use by test suites. If this method is called, then all subsequent calls
//     * to performDrag will terminate immediately and behave as though the object were
//     * dragged to the given location. Calling this method with null cancels this 
//     * behavior and causes performDrag to behave normally. 
//     * 
//     * @param forcedLocation location where objects will be dropped (or null to
//     * cause drag/drop to behave normally).
//     */
//    public static void forceDropLocation(TestDropLocation forcedLocation) {
//        forcedDropTarget = forcedLocation;
//    }

// RAP [rh] DnD not implemented
//    /**
//     * Drags the given item, given an initial bounding rectangle in display coordinates.
//     * Due to a quirk in the Tracker class, changing the tracking rectangle when using the
//     * keyboard will also cause the mouse cursor to move. Since "snapping" causes the tracking
//     * rectangle to change based on the position of the mouse cursor, it is impossible to do
//     * drag-and-drop with the keyboard when snapping is enabled.    
//     * 
//     * @param draggedItem object being dragged
//     * @param sourceBounds initial bounding rectangle for the dragged item
//     * @param initialLocation initial position of the mouse cursor
//     * @param allowSnapping true iff the rectangle should snap to the drop location. This must
//     * be false if the user might be doing drag-and-drop using the keyboard. 
//     *  
//     * @return
//     */
//    static IDropTarget dragToTarget(final Object draggedItem,
//            final Rectangle sourceBounds, final Point initialLocation,
//            final boolean allowSnapping) {
//        final Display display = Display.getCurrent();
//
//        // Testing...immediately 'drop' onto the test target
//        if (forcedDropTarget != null) {
//            Point location = forcedDropTarget.getLocation();
//
//            Control currentControl = SwtUtil.findControl(forcedDropTarget.getShells(), location);
//            return getDropTarget(currentControl, draggedItem, location,
//                    sourceBounds);
//        }
//
//        // Create a tracker.  This is just an XOR rect on the screen.
//        // As it moves we notify the drag listeners.
//        final Tracker tracker = new Tracker(display, SWT.NULL);
//        tracker.setStippled(true);
//
//        tracker.addListener(SWT.Move, new Listener() {
//            public void handleEvent(final Event event) {
//                display.syncExec(new Runnable() {
//                    public void run() {
//                    	// Get the curslor location as a point
//                        Point location = new Point(event.x, event.y);
//
//                        // Select a drop target; use the global one by default
//                    	IDropTarget target = null;
//                    		
//                        Control targetControl = display.getCursorControl();
//
//                        // Get the drop target for this location
//                        target = getDropTarget(targetControl,
//                                draggedItem, location,
//                                tracker.getRectangles()[0]);
//
//                    	// Set up the tracker feedback based on the target
//                        Rectangle snapTarget = null;
//                        if (target != null) {
//                            snapTarget = target.getSnapRectangle();
//
//                            tracker.setCursor(target.getCursor());
//                        } else {
//                            tracker.setCursor(DragCursors
//                                    .getCursor(DragCursors.INVALID));
//                        }
//
//                        // If snapping then reset the tracker's rectangle based on the current drop target 
//                        if (allowSnapping) {
//                            if (snapTarget == null) {
//                                snapTarget = new Rectangle(sourceBounds.x
//                                        + location.x - initialLocation.x,
//                                        sourceBounds.y + location.y
//                                                - initialLocation.y,
//                                        sourceBounds.width, sourceBounds.height);
//                            }
//
//                            // Try to prevent flicker: don't change the rectangles if they're already in
//                            // the right location
//                            Rectangle[] currentRectangles = tracker.getRectangles();
//
//                            if (!(currentRectangles.length == 1 && currentRectangles[0]
//                                    .equals(snapTarget))) {
//                                tracker.setRectangles(new Rectangle[] { snapTarget });
//                            }
//                        }
//                    }
//                });
//            }
//        });
//
//        // Setup...when the drag starts we might already be over a valid target, check this...
//        // If there is a 'global' target then skip the check
//        IDropTarget target = null;
//        Control startControl = display.getCursorControl();
//        
//        if (startControl != null && allowSnapping) {
//            target = getDropTarget(startControl,
//                draggedItem, initialLocation,
//                sourceBounds);
//        }
//
//        // Set up an initial tracker rectangle
//        Rectangle startRect = sourceBounds;
//        if (target != null) {
//            Rectangle rect = target.getSnapRectangle();
//            
//            if (rect != null) {
//                startRect = rect;
//            }
//
//            tracker.setCursor(target.getCursor());
//        } 
//        
//        if (startRect != null) {
//            tracker.setRectangles(new Rectangle[] { Geometry.copy(startRect)});
//        }
//
//        // Tracking Loop...tracking is preformed on the 'SWT.Move' listener registered
//        // against the tracker.
//        
//        // HACK:
//        // Some control needs to capture the mouse during the drag or other 
//        // controls will interfere with the cursor
//        Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
//        if (shell != null) {
//            shell.setCapture(true);
//        }
//
//        // Run tracker until mouse up occurs or escape key pressed.
//        boolean trackingOk = tracker.open();
//
//        // HACK:
//        // Release the mouse now
//        if (shell != null) {
//            shell.setCapture(false);
//        }
//
//        // Done tracking...
//        
//        // Get the current drop target
//        IDropTarget dropTarget = null;
//        Point finalLocation = display.getCursorLocation();
//        Control targetControl = display.getCursorControl();
//        dropTarget = getDropTarget(targetControl, draggedItem,
//                finalLocation, tracker.getRectangles()[0]);
//
//        // Cleanup...
//        tracker.dispose();
//        
//        // if we're going to perform a 'drop' then delay the issuing of the 'finished'
//        // callback until after it's done...
//        if (trackingOk) {
//        	return dropTarget;
//        }
//        else if (dropTarget!= null && dropTarget instanceof IDropTarget2) {
//            // If the target can handle a 'finished' notification then send one
//        	((IDropTarget2)dropTarget).dragFinished(false);
//        }
//        
//        return null;
//    }

// RAP [rh] DnD not implemented
//    /**
//     * Given a list of IDragOverListeners and a description of what is being dragged, it returns
//     * a IDropTarget for the current drop.
//     * 
//     * @param toSearch
//     * @param mostSpecificControl
//     * @param draggedObject
//     * @param position
//     * @param dragRectangle
//     * @return
//     */
//    private static IDropTarget getDropTarget(List toSearch,
//            Control mostSpecificControl, Object draggedObject, Point position,
//            Rectangle dragRectangle) {
//        if (toSearch == null) {
//            return null;
//        }
//
//        Iterator iter = toSearch.iterator();
//        while (iter.hasNext()) {
//            IDragOverListener next = (IDragOverListener) iter.next();
//
//            IDropTarget dropTarget = next.drag(mostSpecificControl,
//                    draggedObject, position, dragRectangle);
//
//            if (dropTarget != null) {
//                return dropTarget;
//            }
//        }
//
//        return null;
//    }

// RAP [rh] DnD not implemented
//    /**
//     * Returns the drag target for the given control or null if none. 
//     * 
//     * @param toSearch
//     * @param e
//     * @return
//     */
//    public static IDropTarget getDropTarget(Control toSearch,
//            Object draggedObject, Point position, Rectangle dragRectangle) {
//    	// Search for a listener by walking the control's parent hierarchy
//        for (Control current = toSearch; current != null; current = current
//                .getParent()) {
//            IDropTarget dropTarget = getDropTarget(getTargetList(current),
//                    toSearch, draggedObject, position, dragRectangle);
//
//            if (dropTarget != null) {
//                return dropTarget;
//            }
//
//            // Don't look to parent shells for drop targets
//            if (current instanceof Shell) {
//                break;
//            }
//        }
//
//        // No controls could handle this event -- check for default targets
//        return getDropTarget(defaultTargets, toSearch, draggedObject, position,
//                dragRectangle);
//    }

    /**
     * Returns the location of the given event, in display coordinates
     * @return
     */
    public static Point getEventLoc(Event event) {
        Control ctrl = (Control) event.widget;
        return ctrl.toDisplay(new Point(event.x, event.y));
    }

}
